package com.flame.shapeville.controller.task;

import com.flame.shapeville.component.task1.shape2D.*;
import com.flame.shapeville.component.task1.shape3D.*;
import com.flame.shapeville.util.Utils;
import javafx.geometry.Point3D;
import javafx.geometry.Pos;
import javafx.scene.*;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.*;
import javafx.scene.shape.*;
import javafx.scene.text.Text;
import javafx.util.Duration;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Controller for Task 1, which involves identifying 2D and 3D shapes.
 * <p>
 * Allows users to select from various shapes and provides interactive visualization.
 *
 * @author Flame Dev Team
 * @version 1.0
 * @since 2025-05-07
 */
public class Task1Controller extends JfTaskController {

    /**
     * Map of 2D shape names to their corresponding visual components.
     */
    private Map<String, JfShape2D> shape2DMap;

    /**
     * Map of 3D shape names to their corresponding visual components.
     */
    private Map<String, JfShape3D> shape3DMap;

    /**
     * Default size used when rendering shapes.
     */
    private double shapeSize;

    /**
     * JavaFX SubScene for displaying 3D content.
     */
    private SubScene scene3D;

    /**
     * VBox container for organizing 3D shape layout.
     */
    private VBox pane3D;

    /**
     * X-coordinate of the mouse at last drag event.
     */
    private double mouseOldX;

    /**
     * Y-coordinate of the mouse at last drag event.
     */
    private double mouseOldY;

    /**
     * Current rotation angle around the Y-axis.
     */
    private double angleX;

    /**
     * Current rotation angle around the X-axis.
     */
    private double angleY;

    /**
     * Group containing the camera for 3D scene manipulation.
     */
    private Group cameraGroup;

    /**
     * Constructor.
     * Initializes 2D and 3D shape maps and sets up 3D rendering pane.
     */
    public Task1Controller() {
        shapeSize = 150.0;
        init2DShape();
        init3DShape();
        init3DPane();
    }

    /**
     * Initializes the controller by setting up UI components and loading the current exercise.
     */
    @Override
    public void init() {
        super.init();
        setExercise();
        flushSwitchButton();
        initNotice();
        initInput();
    }

    /**
     * Sets up input validation and event listeners for user answer submission.
     */
    private void initInput() {
        input.setOnKeyPressed(event -> {
            if (event.getCode() == javafx.scene.input.KeyCode.ENTER) {
                submit();
            }
        });
        Utils.addClickOutsideToLoseFocus(input, event -> input.getParent().requestFocus());
    }

    /**
     * Sets up mouse interaction for 3D scene rotation.
     */
    private void init3DPane() {
        AnchorPane root = new AnchorPane();

        pane3D = new VBox();
        PointLight light1 = new PointLight(Color.WHITE);
        light1.setTranslateX(-500);
        light1.setTranslateY(-500);
        light1.setTranslateZ(-500);

        scene3D = new SubScene(root, 450, 200, true, SceneAntialiasing.BALANCED);

        PerspectiveCamera camera = new PerspectiveCamera();
        camera.setTranslateZ(-100);

        cameraGroup = new Group(camera);
        cameraGroup.setRotationAxis(new Point3D(0, 1, 0));
        cameraGroup.setRotate(0);

        root.getChildren().addAll(light1, pane3D, cameraGroup);
        scene3D.setCamera(camera);

        scene3D.setOnMousePressed(event -> {
            mouseOldX = event.getSceneX();
            mouseOldY = event.getSceneY();
        });

        scene3D.setOnMouseDragged(event -> {
            double dx = event.getSceneX() - mouseOldX;
            double dy = event.getSceneY() - mouseOldY;

            angleY += dx * 0.5;
            angleX += dy * 0.5;

            if (angleX > 90) angleX = 90;
            if (angleX < -90) angleX = -90;

            cameraGroup.setRotate(angleY);
            camera.setRotate(angleX);
            camera.setRotationAxis(new Point3D(1, 0, 0));

            mouseOldX = event.getSceneX();
            mouseOldY = event.getSceneY();
        });

        scene3D.setOnMouseClicked(event -> {
            if (event.getClickCount() == 2) {
                angleX = 0;
                angleY = 0;
                cameraGroup.setRotate(angleY);
                camera.setRotate(angleX);
            }
        });
    }

    /**
     * Initializes 2D shape map with predefined shapes.
     */
    private void init2DShape() {
        shapeSize = 150.0;
        shape2DMap = new HashMap<>();
        shape2DMap.put("circle", new JfCircle(shapeSize));
        shape2DMap.put("rectangle", new JfRectangle(shapeSize));
        shape2DMap.put("triangle", new JfTriangle(shapeSize));
        shape2DMap.put("oval", new JfOval(shapeSize));
        shape2DMap.put("octagon", new JfOctagon(shapeSize));
        shape2DMap.put("square", new JfSquare(shapeSize));
        shape2DMap.put("heptagon", new JfHeptagon(shapeSize));
        shape2DMap.put("rhombus", new JfRhombus(shapeSize));
        shape2DMap.put("pentagon", new JfPentagon(shapeSize));
        shape2DMap.put("hexagon", new JfHexagon(shapeSize));
        shape2DMap.put("kite", new JfKite(shapeSize));
    }

    /**
     * Initializes 3D shape map with predefined shapes.
     */
    private void init3DShape() {
        shapeSize = 100.0;
        shape3DMap = new HashMap<>();
        shape3DMap.put("cube", new JfCube(shapeSize));
        shape3DMap.put("cuboid", new JfCuboid(shapeSize));
        shape3DMap.put("cylinder", new JfCylinder(shapeSize));
        shape3DMap.put("sphere", new JfSphere(shapeSize));
        shape3DMap.put("triangular prism", new JfTriangularPrism(shapeSize));
        shape3DMap.put("square-based pyramid", new JfSquareBasedPyramid(shapeSize));
        shape3DMap.put("cone", new JfCone(shapeSize));
        shape3DMap.put("tetrahedron", new JfTetrahedron(shapeSize));
    }

    /**
     * Handles submission of answer.
     */
    void submit() {
        String answer = input.getText().trim().toLowerCase();
        answerAnimationAndSave(answer);
    }

    /**
     * Sets current exercise and loads associated UI.
     */
    @Override
    void setExercise() {
        curExercise = exercises.get(exPointer);
        if (!curExercise.getAdvanced()) {
            no.setText(curExercise.getNo() + "");
        } else {
            no.setText(curExercise.getNo() + "*");
        }
        loadShape();
        flushInput();
    }

    /**
     * Updates input field state based on exercise status.
     */
    @Override
    void flushInput() {
        if (curExercise.getCompleted()) {
            input.setText(curExercise.getAnswer());
            input.setDisable(true);
            submit.setDisable(true);
        } else {
            input.clear();
            input.setDisable(false);
            submit.setDisable(false);
        }
    }

    /**
     * Loads the appropriate shape based on the current exercise.
     */
    private void loadShape() {
        shape.getChildren().clear();
        if (!curExercise.getAdvanced()) {
            shape.getChildren().add(shape2DMap.get(curExercise.getAnswer().toLowerCase()).getShape());
        } else {
            shape.getChildren().add(scene3D);
            pane3D.getChildren().clear();
            Shape3D shape3D = shape3DMap.get(curExercise.getAnswer().toLowerCase()).getShape();
            shape3D.setTranslateY(50);
            shape3D.setTranslateX(180);
            shape3D.setTranslateZ(-50);
            pane3D.getChildren().add(shape3D);
        }
    }
}
